#ifndef PARSER_H
#define PARSER_H
#include <vector>
#include "common.h"
#include "lexer.h"
#include "byte_code.h"

#ifdef TAG
#undef TAG
#endif
#define TAG "PARSER "

using std::vector;

class RegDistributer
{
public:
    RegDistributer() : curr_reg_pos_(0), max_reg_num_(0) {};
    virtual ~RegDistributer() = default;
    int GetRegisterNum()
    {
        return max_reg_num_;
    }
    int GetRegisterBySym(string sym);
    int AllocTempReg();
    inline void ReleaseTempReg()
    {
        curr_reg_pos_--;
    }
    inline void AddSym(string sym)
    {
        symbols_.emplace_back(sym);
    }
private:
    vector<string> symbols_;
    int curr_reg_pos_;
    int max_reg_num_;
};

class InstSet
{
public:
    InstSet(string file_name);
    virtual ~InstSet();
    void AddInst(ByteCode* inst);
    bool WriteInstToFile();
    void Print();
    RegDistributer& GetRegDis();

private:
    vector<ByteCode*> sets_;
    RegDistributer* reg_dis_;
    string output_file_name_;
};

class Node
{
public:
    Node() = default;
    virtual ~Node();
    virtual string ToString() { return "Node"; };
    virtual void GenerateCode(InstSet& inst_set) = 0;

private:
    vector<Node*> nodes_;
};

class SymbolNode : public Node
{
public:
    SymbolNode(string name) : symbol_name_(name) {};
    void GenerateCode(InstSet& inst_set) override;
    string ToString() override { return symbol_name_; }

private:
    string symbol_name_;
};

class ValNode : public Node
{
public:
    ValNode(int val) : val_(val) {};
    virtual ~ValNode() = default;
    void GenerateCode(InstSet& inst_set);

private:
    int val_;
};

class ExprNode : public Node
{
public:
    ExprNode(Node* result, Node* cal) : result_node_(result), cal_node_(cal) {}
    ~ExprNode()
    {
        delete result_node_;
        delete cal_node_;
    }
    void GenerateCode(InstSet& inst_set) override;
    string ToString() override { return "ExprNode"; }

private:
    Node* result_node_;
    Node* cal_node_;
};

class OpNode : public Node
{
public:
    OpNode(TOKEN token, Node* left, Node* right) : token_(token), left_(left), right_(right) {}
    ~OpNode()
    {
        delete left_;
        delete right_;
    }
    void GenerateCode(InstSet& inst_set);

private:
    TOKEN token_;
    Node* left_;
    Node* right_;
    BYTE_CODE GetOpcodeByToken(TOKEN token);
};

class PrintNode : public Node
{
public:
    PrintNode(Node* sym) : var_(sym) {}
    void GenerateCode(InstSet& inst_set);

private:
    Node* var_;
};

class Parser
{
public:
    Parser(string input, string output = DEFAULT_OUTPUT_FILE);
    virtual ~Parser();
    void Match(TOKEN token);
    void Expr();
    void Print();
    void Parse();

private:
    Lexer* lexer_;
    Node* abs_syntac_tree_;
    Token* curr_token_;
    vector<string*> code_;
    InstSet* inst_set_;
};
#endif // PARSER_H